package com.gitee.minimalismstyle.fresh.common.document.execl

import com.gitee.minimalismstyle.fresh.common.core.conversion.TypeConversion
import com.gitee.minimalismstyle.fresh.common.core.conversion.TypeConversionFactory
import com.gitee.minimalismstyle.fresh.common.core.exception.MessageException
import com.gitee.minimalismstyle.fresh.common.core.spring.SpringContextUtils
import com.gitee.minimalismstyle.fresh.common.document.domain.Cell
import com.gitee.minimalismstyle.fresh.common.document.domain.ExeclDataMap
import com.gitee.minimalismstyle.fresh.common.document.domain.Property
import com.gitee.minimalismstyle.fresh.common.document.util.ExeclUtils
import com.gitee.minimalismstyle.fresh.common.core.validate.ValueValidate
import com.gitee.minimalismstyle.fresh.common.core.validate.ValueValidateFactory
import org.apache.poi.hssf.usermodel.*
import org.apache.poi.ss.usermodel.Sheet
import org.apache.poi.ss.usermodel.Workbook
import com.gitee.minimalismstyle.fresh.common.document.domain.Workbook as wb
import org.apache.poi.hssf.usermodel.HSSFRichTextString

import org.apache.poi.ss.usermodel.Drawing
import org.apache.poi.xssf.usermodel.XSSFRichTextString
import java.io.ByteArrayOutputStream
import java.io.File


object ExeclResolver {

    fun read(wb : wb, file: File): ExeclDataMap{
        var data: ExeclDataMap = ExeclDataMap()
        var workbook = ExeclUtils.getWorkbook(file)
        wb.sheets.forEach { st ->
            val sheet = workbook.getSheetAt(st.index!!)
            readPropertys(st.propertys, sheet, data)
        }

        return data
    }

    private fun readPropertys(propertys: List<Property>, sheet: Sheet, data: MutableMap<String, Any>){
        propertys.forEach { property ->
            if(property.propertys.isEmpty()){
                val cellData = readProperty(property, sheet)
                data.put(property.name!!, cellData)
            }else{
                if (property.start == null){
                    var propertyData: ExeclDataMap = ExeclDataMap()
                    data.put(property.name!!, propertyData)
                    readPropertys(property.propertys, sheet, propertyData)
                }else{
                    val propertyDataList: MutableList<ExeclDataMap> = arrayListOf()
                    var rowIndex:Int = property.start!! -1
                    var isRowEnd = false
                    while (!isRowEnd){
                        var row = sheet.getRow(rowIndex)
                        if(row == null){
                            isRowEnd =  true
                        }else{
                            var isBlankRow = true
                            property.propertys.forEach {
                                if(it.start == null){
                                    it.row = rowIndex+1
                                    val cell = row.getCell(it.col!!)
                                    if(cell != null && !cell.stringCellValue.isNullOrBlank()){
                                        isBlankRow = false
                                    }
                                }

                            }
                            isRowEnd = isBlankRow

                        }
                        rowIndex++
                        if(!isRowEnd && property.end != null){
                            isRowEnd = rowIndex > property.end!!
                        }
                        if(!isRowEnd){
                            var propertyData: ExeclDataMap = ExeclDataMap()
                            readPropertys(property.propertys, sheet, propertyData)
                            propertyDataList.add(propertyData)
                        }


                    }

                    data.put(property.name!!, propertyDataList)

                }
            }

        }
    }

    private fun readProperty(property: Property, sheet: Sheet): Cell {

        val rowIndex: Int = property.row!! - 1
        var row = sheet.getRow(rowIndex)
        if(row == null){
            row = sheet.createRow(rowIndex)
        }

        val colIndex:Int = property.col!!
        var cell = row.getCell(colIndex)

        if(cell == null){
            cell = row.createCell(colIndex)
        }
        var cellData = Cell(sheet.sheetName, rowIndex, colIndex)
        var value: String? = cell.stringCellValue.trim()
        if(value.isNullOrBlank() && property.default != null){
            value = property.default
        }
        property.validates.forEach { validate ->
            val valueValidate: ValueValidate?
            if(validate.method.isNullOrBlank()){
                valueValidate = SpringContextUtils.getBean(validate.bean!!)
            }else{
                valueValidate = ValueValidateFactory.get(validate.method!!)
            }

            if(valueValidate == null){
                throw MessageException(message = "请定义验证器")
            }else{
                val validateResult = valueValidate.check(value, validate.params)
                cellData.valid = validateResult.valid
                if(!validateResult.valid){
                    if(validate.message.isNullOrBlank()){
                        cellData.message = validateResult.message
                    }else{
                        cellData.message = validate.message
                    }
                }
            }
        }

        val typeConversion: TypeConversion<String, *>
        if(property.conversion.isNullOrBlank()){
            typeConversion = TypeConversionFactory.get(String::class.java.name, property.type!!)
                    as TypeConversion<String, *>
        }else{
            typeConversion = SpringContextUtils.getBean(property.conversion!!)
        }

        val typeValue = typeConversion.convert(value, property.pattern)
        cellData.value = typeValue
        return cellData

    }

    fun writeError(data: ExeclDataMap, file: File): ByteArray{
        val workbook = ExeclUtils.getWorkbook(file)
        writeError(data, workbook)
        ByteArrayOutputStream().use {
            workbook.write(it)
            return it.toByteArray()
        }

    }

    fun writeError(data: ExeclDataMap, workbook: Workbook){

        val helper = workbook.creationHelper
        val anchor = helper.createClientAnchor()
        data.values.forEach { v ->
            if(v is Cell){
                val sheet = workbook.getSheet(v.sheetName)
                var row = sheet.getRow(v.row)
                if(row == null){
                    row = sheet.createRow(v.row)
                }
                var cell = row.getCell(v.col)
                if(cell == null){
                    cell = row.createCell(v.col)
                }

                if(v.valid){
                    if(cell.cellComment != null){
                        cell.removeCellComment()
                    }
                }else{
                    val patr: Drawing<*> = sheet.createDrawingPatriarch()
                    val comment = patr.createCellComment(anchor)
                    if(sheet is HSSFSheet){
                        comment.setString(HSSFRichTextString(v.message))
                    }else{
                        comment.setString(XSSFRichTextString(v.message))
                    }

                    cell.cellComment =  comment

                }
            }else if(v is ExeclDataMap){
                writeError(v, workbook)
            }else if(v is List<*>){
                v.forEach {
                    writeError(it as ExeclDataMap, workbook)
                }
            }
        }
    }

}